<?PHP
	/*
		This daemon script looks for new email on an IMAP server. When new
		email is found it sends a notification via XMPP (Jabber/GoogleTalk).
		
		I created this to do Blackberry style notifications of new email on my
		iPhone. Becuase I use BeeJive with push notifications I can leverage that
		to get pop ups on the iPhone.
		
		This application uses the XMPPPHP libarary found at: http://code.google.com/p/xmpphp/
		to interact with the XMPP Server.
		It also requires the PHP-IMAP library be installed.
		
		@author: Brian roy
		@date: 09/25/2009
		
	*/
	
	require_once("xmpp-php-lib/XMPPHP/XMPP.php");
	
	define("CONFFILE", "config");
	define("MSG_QUERY", "ALL UNSEEN");
	
	
	
	// Get the command line arguments.
	
	for($i=1; $i < count($argv); ++$i) {
		//echo "Processing command line arg: ".$argv[$i]."\n";
		$aryParts = explode("=", $argv[$i]);
		//echo "After explode we have: ".count($aryParts)." sections\n";
		if((count($aryParts)) != 2) {
			//echo "Not 2 parts?\n";
			showUsage();
			exit();
		}
		
		$aryParts[0] = trim($aryParts[0]);
		switch ($aryParts[0]) {
			case CONFFILE:
				$strConfigFile = $aryParts[1];
				break 1;
			default:
				showUsage();
				exit();
		}
		
	}
	
	// Check to see if we have all the required elements and fill the optionals if not supplied.
	
	if(!$strConfigFile) {
		showUsage();
		exit();
	}
	
	// Make sure that config file exists...
	
	if(!file_exists($strConfigFile)) {
		echo "The specified configuration file does not exist.\n";
		showUsage();
		exit();
	}
	
	// Include the config file
	
	require_once($strConfigFile);
	
	// Daemonize the process...
	
	#
    $pid = pcntl_fork();
    #
    if($pid == -1) {
            #
            // Something went wrong (handle errors here)
            #
			$strMsg = "ERROR: ".date("r")." Could not daemonize the process... Exiting now...\n";
			error_log($strMsg, 3, LOGFILE);
            exit();
    } elseif($pid == 0) {
            #
            // This part is only executed in the child
            #
           
    } else {
            #
            // This part is only executed in the parent
            #
            exit();
            #
    }
	
	// OK... connect to the IMAP server and our XMPP server
	
	$svrC = "{".SERVER;
	if(USESSL == "yes") $svrC .= ":993/ssl";
	$svrC .= "}".INBOX;
	
	// Now connect to the XMPP Server
	
	$xmpp = new XMPPHP_XMPP(XMPP_SERVER, 5222, XMPP_USER, XMPP_PASSWORD, USERNAME);
	
	try {
		$xmpp->connect();
		$xmpp->processUntil('session_start');
		$xmpp->presence("Notifying you of new email...");
		$xmpp->autoSubscribe(true);
	} catch(XMPPHP_Exception $e) {
		$strMsg = "ERROR: ".date("r")." XMPP Error: ".$e->getMessage()." Exiting now...\n";
		error_log($strMsg, 3, LOGFILE);
		exit();
	}

	// Now we can enter the main loop...
	
	while(true) {
		// Now connect to the server

		$svrI = imap_open($svrC, USERNAME, PASSWORD);
		
		if($svrI === false) {
			// couldn't connect
			$strMsg = "ERROR: ".date("r")." Couldn't connect to the imap server for user: ".USERNAME."\n";
			error_log($strMsg, 3, LOGFILE);
		} else {
			
			
			$rslt = imap_search($svrI, MSG_QUERY);
			if($rslt === false) {
				// No unseen messages
				// Dump the array of already notified UIDs
				unset($aryNotified);
			} else {
				$intNow = time();
				
				foreach($rslt as $rsltID) {
					$rsltML = imap_fetch_overview($svrI, $rsltID);
					foreach($rsltML as $rsltM) {
						if(DEBUG) {
							$strMsg = "DEBUG: ".date("r")." processing message with uid: ".$rsltM->uid." subject: ".$rsltM->subject." from: ".$rsltM->from."\n";
							error_log($strMsg, 3, LOGFILE);
						}
						$intMsgTime = strtotime($rsltM->date);
						
						if(($intNow - OLDEST_NOTIFY) < $intMsgTime) {
							// we notifiy on these unless we've done it before
							//	See if we've notfied for this UID
							
							if(DEBUG) {
								$strMsg = "DEBUG: ".date("r")." processing message with uid: ".$rsltM->uid." This message qualifies for notificaton based on time.\n";
								error_log($strMsg, 3, LOGFILE);
							}
							$blnAlreadyNotified = false;
							foreach($aryNotified as $doneUID) {
								if($rsltM->uid == $doneUID) {
									$blnAlreadyNotified = true;
									if(DEBUG) {
										$strMsg = "DEBUG: ".date("r")." processing message with uid: ".$rsltM->uid." already notified for this message... \n";
										error_log($strMsg, 3, LOGFILE);
									}
									break;
								}
							}
							
							if(! $blnAlreadyNotified) {
								$aryNotified[] = $rsltM->uid;
								// send notification
								if(doFilters($rsltM)) {
									if(DEBUG) {
										$strMsg = "DEBUG: ".date("r")." processing message with uid: ".$rsltM->uid." sending XMPP Notification Now...\n";
										error_log($strMsg, 3, LOGFILE);
									}
									$strNMsg = "You have a new email: ".$rsltM->subject." \nFrom: ".$rsltM->from." \nRecieved On: ".$rsltM->date."\n";
									
									try {
										$xmpp->message(XMPP_NOTIFY, $strNMsg);
									} catch(XMPPHP_Exception $e) {
										$strMsg = "FATAL ERROR: ".date("r")." Caught XMPP Error sending notification.\n";
										error_log($strMsg, 3, LOGFILE);
										$strMsg = "ERROR: ".date("r")." XMPP Error: ".$e->getMessage()."\n";
										error_log($strMsg, 3, LOGFILE);
										exit();
									}
								} else {
									if(DEBUG) {
										$strMsg = "DEBUG: ".date("r")." processing message with uid: ".$rsltM->uid." Message did not meet filter criteria... not notify...\n";
										error_log($strMsg, 3, LOGFILE);
									}
								}
							}
						} else {
							if(DEBUG) {
								$strMsg = "DEBUG: ".date("r")." processing message with uid: ".$rsltM->uid." Message is too old for notification.\n";
								error_log($strMsg, 3, LOGFILE);
							}
						} // end of if time 
					}	// end of inside foreach loop
				} // end foreach message loop
				
				
				
				
				
			}
			
			unset($rslt);
			imap_close($svrI);
		}
		
		// rest a moment
		sleep(NAPTIME);
		$xmpp->presence("Notifying you of new email...");
		$xmpp->autoSubscribe(true);
	}

function doFilters($eml) {
	/*
		This function implements the filters found in the config file.
		Returns True of we Notify, False if not.
		
		Any's applied first... Skips only applied if no Any rules.
		
	*/
	
	global $aryFromAny;
	global $aryFromInAny;
	global $arySubjInAny;
	global $aryFromSkip;
	global $aryFromInSkip;
	global $arySubjInSkip;
	
	$blnFoundAny = false;
	
	if(count($aryFromAny) > 0) {
		$blnFoundAny = true;
		foreach($aryFromAny as $addr) {
			// if the address = any of these we notify
			if($addr == $eml->from) return true;
		}
	}
	if(count($aryFromInAny) > 0) {
		$blnFoundAny = true;
		foreach($aryFromInAny as $addr) {
			if(mb_stristr($eml->from, $addr) !== false) return true;
		}
	}
	if(count($arySubjInAny) > 0) {
		$blnFoundAny = true;
		foreach($arySubjInAny as $subj) {
			if(mb_stristr($eml->subject, $subj) !== false) return true;
		}
	}
	// If there were no any filters do the Skip Filters
	if(!$blnFoundAny) {
		if(count($aryFromSkip) > 0) {
			foreach($aryFromSkip as $addr) {
				if($addr == $eml->from) return false;
			}
		}
		if(count($aryFromInSkip) > 0) {
			foreach($aryFromInSkip as $addr) {
				if(mb_stristr($eml->from, $addr) !== false) return false;
			}
		}
		if(count($arySubjInSkip) > 0) {
			foreach($arySubjInSkip as $subj) {
				if(mb_stristr($eml->subject, $subj) !== false) return false;
			}
		}
		
		// No Skip filters tripped... notify.
		return true;
	} else {
		// There was one or more Any filter and none tripped... no notify.
		return false;
	}
	
	
	
}

function showUsage() {
	
	$strOut = "Usage:\nThis script requires:\nconfig=<path to your config file>\n\nPlease try again...\n";
	echo $strOut;

}

?>